#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import os

from ctools.converters.convbase import NConverterBase
from ctools.converters.conv_ctn import ConvCTN2Gv
from ctools.ly_parser.nodes import *

from ctools.common.strhelper import *
from ctools.common.paramconfig import ParamConfig

import logging
from ctools.logext import *

_rlog, _plog, _mlog, _tlog = get_loggers(__file__, __name__, propagate=False)

from ctools.converters.conv_kn import ConvKeepN2Gv
from ctools.converters.conv_ln import ConvLinkN2Gv
try:
    from ctools.converters.conv_vn import ConvVarN2Gv
except Exception as e:
    _mlog.debug(f'{e}')
    class ConvVarN2Gv(object):
        def __init__(self, *args, **kwargs):
            self.convert = self.process = lambda *args, **kwargs: ''

class ConvFuncN2Gv(NConverterBase):
    def __init__(self, typedecls, typealiases, paramcfg:ParamConfig, funcs):
        self._text = ''

        self.typedecls = typedecls
        self.typealiases = typealiases
        # TODO: remove self._paramcfg
        self._paramcfg = paramcfg
        self.funcs = funcs

        # TODO: reuse the same instances
        self.keep_gen = ConvKeepN2Gv()
        self.link_gen = ConvLinkN2Gv()
        self.var_gen = ConvVarN2Gv(paramcfg)

    def _get_typedecl(self, name:str) -> TypeNode:
        if name in self.typedecls:
            return self.typedecls[name]
        if name in self.typealiases:
            return self._get_typedecl(self.typealiases[name])
        return None

    def _process_keepnode(self, node, args, depth):
        if not isinstance(node, KeepNode):
            return
        self._text += indent_str(depth, node.body + '\n')

    def _process_varnode(self, node, args, depth):
        indent = depth
        text = ''
        typename, varname = node.type, node.name
        typedecl = self._get_typedecl(typename)
        if typedecl is None:
            if typename != 'func_t':
                _mlog.warning(f'can not find {typename} for {node.name}')
            text += varname
        else:
            # TODO:
            text += indent_str(indent, '')
            html = self.var_gen.process_varnode(typedecl, node)
            label = indent_str(indent, html).strip()
            text += f'{varname} [shape=plaintext, label={label}]'

        return text

    def _process_one_node(self, node, args, depth):
        indent = depth
        if isinstance(node, VarNode):
            text = self._process_varnode(node, args, depth)
            self._text += text + '\n\n'
            return

        if isinstance(node, LinkNode):
            text = indent_str(indent, '')
            text += self.link_gen.process(node, indent,
                                          ctrl_direction='', sequence=True)
            self._text += text + '\n\n'
            return

        if isinstance(node, CallNode):
            funcnode = self.funcs[node.name]
            proc = ConvFuncN2Gv(self.typedecls, self.typealiases,
                                    self._paramcfg, self.funcs)
            self._text += proc.process(funcnode)
            return

        if isinstance(node, KeepNode):
            text = self.keep_gen.process(node, indent)
            self._text += text + '\n\n'
            return

        if isinstance(node, CTreeNode):
            conv = ConvCTN2Gv()
            text = conv.convert(node)
            self._text += text + '\n'
            return

        if isinstance(node, FuncNode):
            return

        if isinstance(node, UnkownNode):
            return

        _mlog.warning('unprocessed {}'.format(node))

    def process(self, node:FuncNode) -> str:
        self._text = ''
        node.iter_nodes(self._process_one_node)
        return self._text

    def convert(self, node:FuncNode) -> str:
        return self.process(node)
